<?php

	namespace org\tekuna\test\core;


	use \PHPUnit_Framework_TestCase;

	use org\tekuna\base\Tekuna;
	use org\tekuna\core\application\Application;
	use org\tekuna\core\context\Context;
	use org\tekuna\core\configuration\Configuration;
	use org\tekuna\core\configuration\ConfigurationElement;
	use org\tekuna\core\configuration\ConfigurationAttribute;
	use org\tekuna\core\event\Event;
	use org\tekuna\core\event\EventListener;
	use org\tekuna\core\event\ApplicationRunEvent;
	use org\tekuna\core\event\AbstractEventListener;
	
	class ApplicationTest extends PHPUnit_Framework_TestCase implements Configuration, EventListener {

		private
			$objContext = NULL,
			$objConfigurationElement = NULL,
			$objTestApp = null,
			$objApplicationContext = null,
			$bLoadConfigExecuted = false;
		
		public function setUp() {

			$this -> bLoadConfigExecuted = false;
			$this -> objTestApp = Application :: buildApplication($this);
		}


		public function testRuntimeEnvironment() {

			// assuming the tests were run on the console
			$this -> assertFalse(Application :: isHttpRequest());
			$this -> assertTrue(Application :: isCliRequest());
		}


		public function testApplicationType() {

			// assuming the tests were run on the console
			$this -> assertEquals('org\tekuna\core\application\CliApplication', get_class($this -> objTestApp));
		}


		public function testApplicationRun() {

			global $GLOBAL;
			
			// application was not run
			$this -> assertFalse($this -> bLoadConfigExecuted);
			$this -> assertFalse(isset($GLOBAL['TEKUNA_APPLICATIONTEST_RUN_EVENT_TRIGGERED']));

			// run the application
			$this -> objTestApp -> run();

			// now the application did run
			$this -> assertTrue($this -> bLoadConfigExecuted);
			$this -> assertTrue(isset($GLOBAL['TEKUNA_APPLICATIONTEST_RUN_EVENT_TRIGGERED']));
		}


		public function testConfigInContext() {

			$this -> assertNull($this -> objApplicationContext);

			// run the application
			$this -> objTestApp -> run();

			// now we have the context
			$this -> assertNotNull($this -> objApplicationContext);

			// and the configuration is inside
			$this -> assertTrue($this -> objApplicationContext -> hasConfiguration());
			$this -> assertEquals('org\tekuna\test\core\ApplicationTest', get_class($this -> objApplicationContext -> getConfiguration()));
		}

		
		public function testListenerRegistration() {

			// run the application
			$this -> objTestApp -> run();
			$arrListeners = $this -> objApplicationContext -> getEventManager() -> getRegisteredListeners();
		
			// there must be two registered listeners
			$this -> assertEquals(2, count($arrListeners));
			
			// check listener classes (and order)
			$this -> assertEquals('org\tekuna\test\core\TestListener2', get_class($arrListeners[0]));
			$this -> assertEquals('org\tekuna\test\core\ApplicationTest', get_class($arrListeners[1]));
		}
		
		/**
		 * @expectedException org\tekuna\core\configuration\ConfigurationException
		 */
		public function testNonListenerClassListener() {
			
			$objApp = Application :: buildApplication(new NonListenerClassListenerConfiguration());
			$objApp -> run();
		}
		
		
		/**
		 * @expectedException org\tekuna\core\configuration\ConfigurationException
		 */
		public function testNonExistingClassListener() {
			
			$objApp = Application :: buildApplication(new NonExistingClassListenerConfiguration());
			$objApp -> run();
		}

		
		/**
		 * @expectedException org\tekuna\core\configuration\ConfigurationException
		 */
		public function testNoListener() {
			
			$objApp = Application :: buildApplication(new NoListenerConfiguration());
			$objApp -> run();
		}


		public function testClassLoaderRegistration() {

			$objTekuna = Tekuna :: getInstance();
	
			// memorize the old class loader count
			$iOrigCountClassloaders = count($objTekuna -> getRegisteredClassLoaders());
			
			// run the application
			$this -> objTestApp -> run();
			
			// there must be one more registered class loader
			$this -> assertEquals($iOrigCountClassloaders+1, count($objTekuna -> getRegisteredClassLoaders()));
		}
		
		
		// methods that fulfill the interface Configuration
		public function loadConfiguration(Context $objContext) {

			$this -> bLoadConfigExecuted = true;
			$this -> objApplicationContext = $objContext;
		}

		public function getRootElement() {

			$objRootElement = new ConfigurationElement('core', 'application', '');
			
			$objListenerElement = new ConfigurationElement('core', 'listener', '');
			$objListenerElement -> addAttribute(new ConfigurationAttribute('core', 'class', 'org\tekuna\test\core\ApplicationTest'));
			$objRootElement -> addChildElement($objListenerElement);
			
			$objListenerElement2 = new ConfigurationElement('core', 'listener', '');
			$objListenerElement2 -> addAttribute(new ConfigurationAttribute('core', 'class', 'org\tekuna\test\core\TestListener2'));
			$objListenerElement2 -> addAttribute(new ConfigurationAttribute('core', 'weight', 'min'));
			$objRootElement -> addChildElement($objListenerElement2);
			
			$objListenerElement = new ConfigurationElement('core', 'classloader', '');
			$objListenerElement -> addAttribute(new ConfigurationAttribute('core', 'dir', 'src'));
			$objRootElement -> addChildElement($objListenerElement);
			
			return $objRootElement;
		}

		// methods that fulfill the interface EventHandler
		public function setApplicationContext(Context $objContext) {
			
			$this -> objContext = $objContext;
		}
		
		public function getApplicationContext() {
			
			return $this -> objContext;
		}
		
		public function setConfigurationElement(ConfigurationElement $objElement) {
			
			$this -> objConfigurationElement = $objElement;
		}
		
		public function getConfigurationElement() {
			
			return $this -> objConfigurationElement;
		}
		
		public function handlesEvent(Event $objEvent) {
			
			return $objEvent instanceof ApplicationRunEvent;
		}
		
		public function handleEvent(Event $objEvent) {

			global $GLOBAL;
			$GLOBAL['TEKUNA_APPLICATIONTEST_RUN_EVENT_TRIGGERED'] = true;
		}
	}

	
	class TestListener2 extends AbstractEventListener {

		public function setApplicationContext(Context $objContext) {

		}

		public function handlesEvent(Event $objEvent) {
			
			return $objEvent instanceof ApplicationRunEvent;
		}
		
		public function handleEvent(Event $objEvent) {

		}
	}
	
	
	class NonListenerClassListenerConfiguration implements Configuration {
		
		public function loadConfiguration(Context $objContext) {

		}

		public function getRootElement() {

			$objRootElement = new ConfigurationElement('core', 'application', '');
			
			$objListenerElement = new ConfigurationElement('core', 'listener', '');
			$objListenerElement -> addAttribute(new ConfigurationAttribute('core', 'class', 'org\tekuna\test\core\NonListenerClassListenerConfiguration'));
			$objRootElement -> addChildElement($objListenerElement);
			
			return $objRootElement;
		}
	}

	
	class NonExistingClassListenerConfiguration implements Configuration {
		
		public function loadConfiguration(Context $objContext) {

		}

		public function getRootElement() {

			$objRootElement = new ConfigurationElement('core', 'application', '');
			
			$objListenerElement = new ConfigurationElement('core', 'listener', '');
			$objListenerElement -> addAttribute(new ConfigurationAttribute('core', 'class', 'org\tekuna\test\core\NonExistingClassFooBarBaz'));
			$objRootElement -> addChildElement($objListenerElement);
			
			return $objRootElement;
		}
	}
	
	class NoListenerConfiguration implements Configuration {
		
		public function loadConfiguration(Context $objContext) {

		}

		public function getRootElement() {

			$objRootElement = new ConfigurationElement('core', 'application', '');
			return $objRootElement;
		}
	}
	
	